using codeparser.net.Exceptions;

using System;
using System.Collections.Generic;
using System.Xml;

namespace codeparser.net
{
    /// <summary>
    /// Represents a parser configuration.
    /// </summary>
    public class ParserConfiguration
    {
        /// <summary>
        /// Contains the tag configurations.
        /// </summary>
        private List<TagConfiguration> _tagConfigurations;

        /// <summary>
        /// Contains the expression replacements.
        /// </summary>
        private List<ExpressionReplacement> _expressionReplacements;

        /// <summary>
        /// Contains whether an exception is thrown when an invalid tag is found.
        /// </summary>
        private bool _throwExceptionOnInvalidTag;

        /// <summary>
        /// Gets the tag configurations.
        /// </summary>
        /// <value>The tags.</value>
        public List<TagConfiguration> TagConfigurations
        {
            get
            {
                return this._tagConfigurations;
            }
        }

        /// <summary>
        /// Gets the expression replacements.
        /// </summary>
        /// <value>The expression replacements.</value>
        public List<ExpressionReplacement> ExpressionReplacements
        {
            get
            {
                return this._expressionReplacements;
            }
        }

        /// <summary>
        /// Gets or sets whether an exception should be thrown when an invalid tag is found.
        /// </summary>
        /// <value><c>true</c> if an exception shall be thrown; otherwise, <c>false.</c></value>
        public bool ThrowExceptionOnInvalidTag
        {
            get
            {
                return this._throwExceptionOnInvalidTag;
            }

            set
            {
                this._throwExceptionOnInvalidTag = value;
            }
        }

        /// <summary>
        /// Gets the tag configuration.
        /// </summary>
        /// <param name="tag">The tag.</param>
        /// <returns>The tag configuration.</returns>
        public TagConfiguration GetTagConfiguration(string tag)
        {
            foreach (TagConfiguration tagConfiguration in this._tagConfigurations)
            {
                if (tagConfiguration.Tag == tag)
                {
                    return tagConfiguration;
                }
            }

            if (this._throwExceptionOnInvalidTag)
            {
                throw new InvalidTagException();
            }

            return new TagConfiguration(tag, "[" + tag + "]");
        }

        /// <summary>
        /// Loads the configuration from an XML file.
        /// </summary>
        /// <param name="configurationFile">The XML configuration file.</param>
        public void LoadConfigurationFromXml(string configurationFile)
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load(configurationFile);

            XmlNode throwExceptionOnInvalidTag = xmlDocument.SelectSingleNode("/configuration/parser/throwExceptionOnInvalidTag/@value");
            if (throwExceptionOnInvalidTag != null)
            {
                Boolean.TryParse(throwExceptionOnInvalidTag.Value, out this._throwExceptionOnInvalidTag);
            }

            XmlNodeList nodes = xmlDocument.SelectNodes("/configuration/parser/expressionReplacements/expressionReplacement");
            foreach (XmlNode node in nodes)
            {
                // Get the expression,
                string expression = node.SelectSingleNode("@expression").Value;
                expression = expression.Replace("\\n", "\n");

                // Get the replacement.
                string replacement = node.SelectSingleNode("@replacement").Value;
                replacement = replacement.Replace("\\n", "\n");

                // Build the expression replacement.
                ExpressionReplacement expressionReplacement =
                    new ExpressionReplacement(expression, replacement);
                this._expressionReplacements.Add(expressionReplacement);
            }

            nodes = xmlDocument.SelectNodes("/configuration/tags/tag");
            foreach (XmlNode node in nodes)
            {
                string tag = node.SelectSingleNode("@name").InnerText;
                string replacement = node.SelectSingleNode("@replacement").InnerText;
                string alternativeReplacement = node.SelectSingleNode("@alternativeReplacement") != null ? node.SelectSingleNode("@alternativeReplacement").InnerText : null;
                try
                {
                    bool parseContent = Boolean.Parse(node.SelectSingleNode("@parseContent").InnerText);

                    try
                    {
                        string contentHighlighter = node.SelectSingleNode("@contentHighlighter").InnerText;
                        this._tagConfigurations.Add(new TagConfiguration(tag, replacement, alternativeReplacement, parseContent, contentHighlighter));
                    }
                    catch
                    {
                        this._tagConfigurations.Add(new TagConfiguration(tag, replacement, alternativeReplacement, parseContent));
                    }
                }
                catch (NullReferenceException)
                {
                    this._tagConfigurations.Add(new TagConfiguration(tag, replacement, alternativeReplacement));
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParserConfiguration"/> class.
        /// </summary>
        public ParserConfiguration()
            : this(null, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParserConfiguration"/> class.
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        public ParserConfiguration(string configurationFile)
            : this(configurationFile, true)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParserConfiguration"/> class.
        /// </summary>
        /// <param name="throwExceptionOnInvalidTag">If set to <c>true</c>, an exception
        /// will be thrown when an invalid tag is encountered. Otherwise, the tag gets
        /// ignored.</param>
        public ParserConfiguration(bool throwExceptionOnInvalidTag)
            : this(null, throwExceptionOnInvalidTag)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParserConfiguration"/> class.
        /// </summary>
        /// <param name="configurationFile">The configuration file.</param>
        /// <param name="throwExceptionOnInvalidTag">If set to <c>true</c>, an exception
        /// will be thrown when an invalid tag is encountered. Otherwise, the tag gets
        /// ignored.</param>
        public ParserConfiguration(string configurationFile, bool throwExceptionOnInvalidTag)
        {
            this._tagConfigurations = new List<TagConfiguration>();
            this._expressionReplacements = new List<ExpressionReplacement>();
            this._throwExceptionOnInvalidTag = throwExceptionOnInvalidTag;

            if (!String.IsNullOrEmpty(configurationFile))
            {
                this.LoadConfigurationFromXml(configurationFile);
            }
        }
    }
}